package com.asen.commons.core.meta.dao;

import com.asen.commons.core.meta.enums.AggrFuncEnum;
import com.asen.commons.core.meta.enums.FilterRelationEnum;
import com.asen.commons.core.meta.enums.OrderSymbolEnum;
import com.asen.commons.utils.BaseAssert;
import com.asen.commons.utils.StringUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * DAO条件
 *
 * @author ls
 * @version 1.0
 * @date 2024-04-09
 */
public class Condition extends FilterGroup {

    /**
     * 是否分页（默认查询不分页）
     */
    private boolean isPage = false;

    /**
     * 页码
     */
    private int pageNum = 1;

    /**
     * 每页查询记录数
     */
    private int pageSize = 20;

    /**
     * 实体类型
     */
    private String modelType;

    /**
     * 排序项
     */
    private List<OrderItem> orderItems = new ArrayList<OrderItem>();

    private List<String> groupByItems = new ArrayList<String>();

    private List<String> collectByItems = new ArrayList<String>();

    /**
     * 查询项集合
     */
    private SelectCollection selectItems = new SelectCollection();

    /**
     * 聚合函数
     */
    private List<AggrFuncItem> aggrFuncItems = new ArrayList<AggrFuncItem>();

    /**
     * 查询限制
     */
    private LimitItem limitItem;

    @JsonIgnore
    private Map<String, Object> reserveMap = new HashMap<>();

    public Condition() {
        super();
    }

    public Condition(FilterRelationEnum filterRelation) {
        super(filterRelation);
    }

    public Condition(String modelType) {
        super();
        this.modelType = modelType;
    }

    /**
     * 条件为enabled
     */
    public void equalEnabled() {
        equal("isEnabled", Boolean.TRUE);
    }

    public void noEqualEnabled() {
        notEqual("isEnabled", Boolean.TRUE);
    }

    /**
     * 条件为disabled
     */
    public void disabled() {
        equal("isEnabled", Boolean.FALSE);
    }

    public void notDisabled() {
        notEqual("isEnabled", Boolean.FALSE);
    }

    /**
     * 添加任意多个顺序排序，支持关联属性。如：
     * orderByAsc("name", "creator.number");
     *
     * @param fieldNames
     */
    public void orderByAsc(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            addOrderItem(fieldName, OrderSymbolEnum.ASC);
        }
    }

    /**
     * 添加任意多个倒序排序，支持关联属性。如：
     * orderByAsc("name", "creator.number");
     *
     * @param fieldNames
     */
    public void orderByDesc(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            addOrderItem(fieldName, OrderSymbolEnum.DESC);
        }
    }

    /**
     * 添加排序，支持关联属性。如：
     * addOrderItem("name", OrderSymbolEnum.ASC);
     * addOrderItem("creator.number", OrderSymbolEnum.DESC);
     *
     * @param fieldName
     * @param symbol
     */
    public void addOrderItem(String fieldName, OrderSymbolEnum symbol) {
        OrderItem item = new OrderItem(fieldName, symbol);

        if (orderItems.contains(item)) {
            return;
        }

        orderItems.add(item);
    }

    /**
     * 删除排序项
     *
     * @param fieldName
     */
    public void removeOrderItem(String fieldName) {
        if (StringUtils.isEmpty(fieldName)) {
            return;
        }

        orderItems.removeIf(x -> x.getFieldName().equals(fieldName));
    }

    /**
     * 添加查询字段，支持关联属性。 如： select("name"); select("creator.number");
     *
     * @param fieldNames
     */
    public void select(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            addSelect(fieldName);
        }
    }

    public void selectExclude(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            selectItems.addExclude(fieldName);
        }
    }

    public void addSelect(String fieldName) {
        selectItems.addSelect(fieldName);
    }

    public void removeSelect(String fieldName) {
        selectItems.removeSelect(fieldName);
    }

    /**
     * 支持自定义select项，支持复杂查询项sql或表达式，实体属性以${XXX}格式引用
     * 如：condition.customSelect("${bizType} = case when ${type} in (1, 2, 3) then '02' else '01' end")
     *
     * @param custom
     */
    public void customSelect(String custom) {
        selectItems.addCustom(custom);
    }

    public void groupBy(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            addGroupBy(fieldName);
        }
    }

    public void addGroupBy(String fieldName) {
        if (groupByItems.contains(fieldName)) {
            return;
        }

        groupByItems.add(fieldName);
    }

    public void removeGroupBy(String fieldName) {
        groupByItems.remove(fieldName);
    }

    public void collectBy(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            if (collectByItems.contains(fieldName)) {
                continue;
            }
            collectByItems.add(fieldName);
        }
    }

    public List<String> getCollectByItems() {
        return collectByItems;
    }

    public void count(String fieldName) {
        count(fieldName, fieldName);
    }

    public void count(String fieldName, String alias) {
        aggrFuncItems.add(new AggrFuncItem(fieldName, AggrFuncEnum.COUNT, alias));
    }

    public void sum(String fieldName) {
        sum(fieldName, fieldName);
    }

    public void sums(String... fieldNames) {
        BaseAssert.notEmpty(fieldNames);

        for (String fieldName : fieldNames) {
            if (aggrFuncItems.contains(fieldName)) {
                continue;
            }
            AggrFuncItem funcItem = new AggrFuncItem(fieldName, AggrFuncEnum.SUM, fieldName);
            aggrFuncItems.add(funcItem);
        }
    }

    public void sum(String fieldName, String alias) {
        aggrFuncItems.add(new AggrFuncItem(fieldName, AggrFuncEnum.SUM, alias));
    }

    public void avg(String fieldName) {
        avg(fieldName, fieldName);
    }

    public void avg(String fieldName, String alias) {
        aggrFuncItems.add(new AggrFuncItem(fieldName, AggrFuncEnum.AVG, alias));
    }

    public void max(String fieldName) {
        max(fieldName, fieldName);
    }

    public void max(String fieldName, String alias) {
        aggrFuncItems.add(new AggrFuncItem(fieldName, AggrFuncEnum.MAX, alias));
    }

    public void min(String fieldName) {
        min(fieldName, fieldName);
    }

    public void min(String fieldName, String alias) {
        aggrFuncItems.add(new AggrFuncItem(fieldName, AggrFuncEnum.MIN, alias));
    }

    public void limit(Integer rows) {
        limit(null, rows);
    }

    public void limit(Integer offset, Integer rows) {
        limitItem = new LimitItem(offset, rows);
    }

    public LimitItem getLimitItem() {
        return limitItem;
    }

    public List<AggrFuncItem> getAggrFuncItems() {
        return aggrFuncItems;
    }

    public void addGroupConcat(String fieldName) {
        addGroupConcat(fieldName, null);
    }

    public void addGroupConcat(String fieldName, String alias) {
        if (StringUtils.isBlank(fieldName)) {
            return;
        }

        if (StringUtils.isBlank(alias)) {
            alias = fieldName;
        }

        StringBuilder custom = new StringBuilder();
        custom.append("GROUP_CONCAT(${")
                .append(fieldName)
                .append("}) AS ")
                .append(alias);
        customSelect(custom.toString());
    }

    @Override
    public void reset() {
        super.reset();
        isPage = false;
        pageNum = 1;
        pageSize = 20;
        selectItems.clear();
        limitItem = null;
        orderItems.clear();
        groupByItems.clear();
        collectByItems.clear();
        aggrFuncItems.clear();
    }

    public void setCollectByItems(List<String> collectByItems) {
        this.collectByItems = collectByItems;
    }

    public boolean isPage() {
        return isPage;
    }

    public void setPage(boolean isPage) {
        this.isPage = isPage;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public String getModelType() {
        return modelType;
    }

    public void setModelType(String modelType) {
        this.modelType = modelType;
    }

    public SelectCollection getSelectItems() {
        return selectItems;
    }

    public List<OrderItem> getOrderItems() {
        return orderItems;
    }

    public List<String> getGroupByItems() {
        return groupByItems;
    }

    public ConditionBuilder builder() {
        return new ConditionBuilder(this);
    }

    @JsonIgnore
    public void put(String key, Object value) {
        reserveMap.put(key, value);
    }

    @JsonIgnore
    public Object get(String key) {
        return reserveMap.get(key);
    }

    @JsonIgnore
    public String getString(String key) {
        Object val = get(key);
        return val == null ? null : val.toString();
    }

    @JsonIgnore
    public Long getLong(String key) {
        Object val = get(key);
        return val == null ? null : Long.valueOf(val.toString());
    }

    @Override
    public String toString() {
        return "Condition{" +
                "isPage=" + isPage +
                ", pageNum=" + pageNum +
                ", pageSize=" + pageSize +
                ", filterRelation=" + getFilterRelation() +
                ", filterGroups=" + getFilterGroups() +
                ", filterItems=" + getFilterItems() +
                ", orderItems=" + orderItems +
                ", selectItems=" + selectItems +
                ", aggrFuncItems=" + aggrFuncItems +
                '}';
    }
}
